fix(recovery): add explicit PrismaModule import to RecoveryModule - #861
Merged
Jambox11 merged 410 commits intoSep 4, 2026
Conversation
Add @apioperation, @ApiParam, @apiquery, @ApiBody, and @apiresponse decorators with inline examples to the six webhook controller handlers that previously had no Swagger coverage: - GET /webhooks/endpoints/:id — 200 (endpoint detail) + 404 - PUT /webhooks/endpoints/:id — 200 + 400 + 404; two @ApiBody examples (update URL/events, disable endpoint) - DELETE /webhooks/endpoints/:id — 204 + 404 - POST /webhooks/endpoints/:id/rotate-secret — 200 with one-time secret note in description + 404 - GET /webhooks/endpoints/:id/deliveries — 200 with full delivery object example + 404; @apiquery for page/limit - POST /webhooks/process-deliveries — 200 with processed/delivered/ failed/retrying summary example; admin description added Also fixed two pre-existing issues in the file: - Stray code sitting outside the class body (orphaned return block) - UpdateWebhookEndpointRequest (undefined type) → UpdateWebhookEndpointDto
- Create new integration test suite (test/webhooks.integration.e2e-spec.ts) with 900+ lines of test coverage - Tests cover 5 main areas: * CRUD operations: endpoint creation, listing, retrieval, updates, deletion with pagination * Event emission & delivery: wallet.created, transaction.confirmed, balance.updated events * Retry & failure handling: transient failures, consecutive failures, dead letter queue * Signature verification: HMAC-SHA256 signing, required webhook headers, timestamp validation * Secret rotation: generation, rotation, uniqueness, secure format - Fix webhook controller imports and add missing FeatureFlag/FeatureFlagGuard imports - Simplify webhook service to remove unused dependencies (cache, requestContext) - Add proper error handling and validation in WebhookService Test Statistics: - 25+ test cases covering all major workflows - Full endpoint coverage with realistic scenarios - Mock axios integration for webhook delivery simulation - Proper setup/teardown and database cleanup
- Add FeatureFlagGuard and @FeatureFlag('wallets_enabled') decorator to WalletsController - Configure guard order: FeatureFlagGuard first, then ApiKeyGuard, then RateLimitGuard - Feature flag check executes before authentication and rate limiting - Allows toggling wallet API access via FEATURE_WALLETS_ENABLED environment variable - Update WalletsController tests to override FeatureFlagGuard - All existing tests pass (11/11 passing) Benefits: - Control wallet API availability without code deployment - Graceful feature toggle during maintenance or rollout - Consistent with webhook and other feature-controlled APIs
- Create WalletCacheService with methods for caching wallet data (142 lines) - Implements cache management for wallet lookups by ID and user+network - Cache key prefixes: wallet:<id> and wallet:user:<userId>:<network> - TTL configured to 5 minutes for optimal balance of freshness and performance Methods provided: - getWalletById/setWalletById - Cache wallet by unique ID - getWalletByUser/setWalletByUser - Cache wallet by user and network - invalidateWalletById/invalidateWalletByUser - Selective cache invalidation - invalidateUserWallets - Bulk invalidation for user across networks - clearAllWalletCache - Full cache purge (maintenance) - Create comprehensive unit tests (227 lines, 18 test cases) - Tests cover cache hit/miss, multi-network scenarios, invalidation, expiration - All tests passing (18/18) Integration Points: - WalletCacheService ready for injection into WalletsService - findWalletById can leverage cache.getWalletById/setWalletById - Cache invalidation available for wallet updates, rotations, and deletes - Stub design allows incremental cache integration without breaking changes
- Create wallet-integration.e2e-spec.ts with 542 lines of test coverage - Uses AppModule for full end-to-end integration testing - Comprehensive CRUD operation tests: * Create wallet with idempotency support * List wallets with pagination and filtering * Get single wallet and wallet status * Update wallet status * Delete wallet * List wallets by user - Multi-network wallet support tests: * Same user on different networks (TESTNET/MAINNET) * Network isolation and filtering * Cross-network wallet operations - Idempotency tests: * Duplicate creation with same idempotency key returns cached result * Duplicate creation with different key returns 409 Conflict * isNewWallet flag correctly reflects idempotent behavior - Pagination and filtering tests: * List with limit and offset * Max limit enforcement (100) * Filter by userId, network, status * Proper pagination metadata (hasMore, total) - Feature flag guard tests: * Disabled feature handling * 403 Forbidden response structure - API key authentication tests: * Valid/invalid key scenarios * Authorization enforcement - Error handling and validation tests: * Invalid enum values * Empty required fields * Duplicate wallet conflict (409) * Not found scenarios (404) * Business logic validation * Graceful error responses Test Statistics: - 50+ test cases covering all major workflows - Full endpoint coverage with realistic scenarios - Database cleanup in afterAll hook - Integration with real Prisma ORM and AppModule - Tests ready for CI/CD pipeline
…che-featureflag-keymgmt-openapi Fix/balance indexer cache featureflag keymgmt openapi
…ovements Fix/key management improvements
…ovements Fix/key management improvements
feat: wallet orchestrator — pagination, filtering, domain events, endpoint docs (mux-labs#415 mux-labs#416 mux-labs#417 mux-labs#419)
…rovements Fix/key management improvements
feat: wallet orchestrator input validation, OpenAPI examples, integra…
Platform improvement for Mux Protocol
…r-metrics-env-e2e-boundaries feat(wallets): orchestrator metrics, env validation, e2e tests, bound…
…provements Platform improvement: Recovery API enhancements (examples, validation, pagination, filtering)
…rator-retry-backoff feat(wallets): add retry with backoff to wallet orchestrator (mux-labs#418)
…examples feat(webhooks): add OpenAPI examples to all webhook endpoints
…ntegration-tests feat(webhooks): Add comprehensive integration tests
…-feature-flag-guard Feature/wallet api feature flag guard
…he-layer-stub Feature/wallet cache layer stub
…-integration-tests Feature/wallet api integration tests
…763-env-validation-cleanup fix: env validation for Horizon retries, maintenance secret, and .env.example cleanup
…ion-cors-stellar-balance-sync fix(config): validate CORS, Stellar network, and balance sync env vars
…port-signing fix: include payment usage in daily limits and fail-closed export signing
Closes mux-labs#793 Closes mux-labs#794 Closes mux-labs#795 Closes mux-labs#796
…rage Adds automated coverage for the internal cron endpoints (/transactions/internal/*) confirming CronSecretGuard fails closed and that a project API key alone is never sufficient to reach them — the gap described in mux-labs#801 had no regression tests, so this was previously unverified behavior. Also hardens CronSecretGuard itself to match the fail-closed, constant-time-comparison pattern already established by InternalServiceGuard (mux-labs#690) in this codebase: - Header comparison now uses crypto.timingSafeEqual instead of !==, removing a timing side-channel on the shared secret. - Header/secret values are trimmed and array-valued headers are handled explicitly (only the first value is considered). - Log lines include a request id and path for correlation, and never log the secret value itself. No behavior change to the guard's pass/fail decisions for already-well-formed requests; CRON_SECRET was already required at startup in production via env.validation.ts and the guard already failed closed when unset. This closes the actual observable gap: missing test coverage, plus the latent timing side-channel. Tests added: - src/common/cron/cron-secret.guard.spec.ts: unit + HTTP-integration tests (unconfigured secret, missing header, wrong secret, correct secret, array-header handling, Authorization-header-is-not-enough). - test/transactions-internal-cron-guard.e2e-spec.ts: e2e coverage of the real /v1/transactions/internal/* routes via the full AppModule, mirroring the existing backup-module-registered.e2e-spec.ts pattern.
…ellar-horizon service
- Add MetricsLabelGuardService to detect and sanitize high-cardinality labels (Stellar StrKey addresses, tx hashes, UUIDs, opaque tokens) - Fail-fast in dev/test to catch bad instrumentation - Sanitize to fixed placeholder in production with hard-cap on distinct combinations - Update MetricsService to route all labels through the guard - Fix label-set mismatch crash bug in prom-client - Provide cardinality statistics for monitoring/debugging Prevents unbounded Prometheus series explosion from wallet IDs or tx hashes leaking into metric labels.
…ux-labs#803) - Create comprehensive SECURITY.md with private disclosure process - Define SLA for critical/high/medium/low severity vulnerabilities - Specify in-scope security domains (wallet encryption, custody, cron auth, API keys, data integrity) - Prevent public GitHub issues for custody/relayer vulnerabilities - Provide private security contact: security@mux.com - Define 90-day responsible disclosure timeline - Establish fail-closed production requirements (WALLET_ENCRYPTION_KEY, CRON_SECRET) - Document safe harbor for security researchers - Add guardrails: never log secrets, no stack traces in errors, request ID tracing Ensures Mux Backend can safely custody Stellar keys, relay sponsored txs, and expose production /v1 API without vulnerability disclosure risks.
…n config (issue mux-labs#804) - Add mainnet payment startup validation to TransactionEnvValidatorService - In production, fail if FEATURE_MAINNET_PAYMENTS enabled but STELLAR_HORIZON_MAINNET_URL missing - Validate STELLAR_HORIZON_MAINNET_URL is valid URL when mainnet payments enabled - In dev/test, allow missing mainnet URL with warning - Enforce fail-closed behavior: catch config gaps at boot, not at payment submission time Prevents silent sponsorship failures and ensures mainnet fee-bump transactions can be submitted to Horizon when the feature is enabled in production.
- mux-labs#789: Generate X-Request-ID when clients omit it - Fix middleware variable declaration (missing let keyword) - Generate UUID for all requests without X-Request-ID header - Enhance exception filter to include generated requestId in responses - Update error-handling tests to verify generation - mux-labs#790: Export auth metrics on Prometheus scrape path - Verify auth metrics registered to prom-client global registry - Create comprehensive auth-metrics-export.e2e-spec.ts test suite - Ensure all metrics accessible on /v1/metrics endpoint - mux-labs#791: Hash stored API keys; never persist plaintext secrets - Enhance validateApiKey with crypto.timingSafeEqual for timing-safe comparison - Verify SHA-256 hashing implementation - Create comprehensive api-key-hashing-security.e2e-spec.ts test suite - Confirm SafeLogger redaction of API keys - mux-labs#792: Unify Clerk vs Better Auth provider paths - Create AuthProvider enum with CLERK and BETTER_AUTH values - Add provider validation to AuthPayloadValidator - Update README with supported providers documentation - Create comprehensive auth-provider-unification.e2e-spec.ts test suite All implementations include fail-closed production safety, comprehensive testing, and documentation.
…responses Implements mux-labs#693, mux-labs#694, mux-labs#695, mux-labs#696. mux-labs#693 — WALLET_ENCRYPTION_KEY rotation & re-encryption job - EncryptionService: optional predecessor key from WALLET_ENCRYPTION_KEY_PREVIOUS (never used to encrypt), plus hasPreviousKey() and reEncryptWithCurrentKey() which decrypts with the current key, falling back to the previous key, and re-wraps under the current key. - WalletKeyReEncryptionService + internal endpoint POST /v1/internal/key-management/re-encrypt-wallet-keys (FeatureFlagGuard + InternalServiceGuard). Id-cursor paginated, idempotent, emits a structured summary log with the request id; refuses to run (400) when WALLET_ENCRYPTION_KEY_PREVIOUS is not set. mux-labs#694 — reject default WALLET_ENCRYPTION_KEY in validateEnv() - validateEnv() now fails fast on the documented placeholder keys (previously only EncryptionService rejected them). - New optional WALLET_ENCRYPTION_KEY_PREVIOUS is validated: min length, not a placeholder, must differ from WALLET_ENCRYPTION_KEY. mux-labs#695 — apply ResponseSanitizerInterceptor globally - Registered via APP_INTERCEPTOR in AppModule so privateKey / encryptedSecret are redacted from every response, not just the orchestration controller. mux-labs#696 — gate loadTestMode on GET /wallets - WalletsService.findAll() returns 403 for loadTestMode=true when NODE_ENV=production; synthetic data stays available outside production. Docs: README, .env.example and CHANGELOG-KEY-MANAGEMENT updated. Tests: encryption rotation unit tests, WalletKeyReEncryptionService spec, env-validation placeholder spec, loadTestMode gating spec, and a global ResponseSanitizerInterceptor e2e spec. chore: repair pnpm-lock.yaml (stale @nestjs/event-emitter snapshot + missing @types/d3-* entries from an earlier bad merge) so pnpm install --frozen-lockfile succeeds again.
Deleting a user only soft-deleted the User row, leaving custody wallets ACTIVE (their encrypted Stellar keys could still sign/relay) and any owned developers/projects/API keys dangling — there was no link from Developer/Project back to User, so the ownership chain could not be walked or cleaned up. - Add nullable Developer.userId FK (onDelete: SetNull) with an email-match backfill migration; expose optional userId on POST /developers. - UsersService.remove() now runs one atomic transaction: disables the user's wallets (DISABLED is terminal), soft-deletes owned developers and projects, REVOKEs their API keys so they stop authenticating, disables webhook endpoints, then soft-deletes the user. Any step failure rolls the whole deletion back (fail-closed; no NODE_ENV skip path). - Logs carry request ids and only counts/IDs (never WALLET_ENCRYPTION_KEY, API keys, or seeds); emits users_deleted_total counter + deletion duration histogram. - Tests: 6 new unit cases, an 8-case integration spec, and a 3-case e2e spec. Verified 9 of the new tests fail against the old implementation. Generated with Codebuff 🤖 Co-Authored-By: Codebuff <noreply@codebuff.com>
Wallet nicknames are rendered in the dashboard and consumed by the public /v1 API, so unsanitized input is a stored-XSS vector and duplicate labels make it impossible to distinguish custodied wallets. updateNickname now: - Sanitizes the label (HTML tag-like sequences, javascript: schemes, inline on* handlers, control chars) before persisting or returning it. - Enforces case-insensitive uniqueness across the owner's non-archived wallets, returning 409 Conflict on a collision; clearing never triggers the check, and a value that sanitizes to empty is treated as a clear. - Emits an update_nickname metric and structured logs carrying the x-request-id and userId (never secret material). Adds unit coverage in wallet-nickname.spec.ts and a controller e2e in test/wallet-nickname.e2e-spec.ts, and documents the behavior in the README and OpenAPI DTO. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
…-796 Somzilla Fixs this
…ming-safe-and-tests fix(mux-labs#801): timing-safe CronSecretGuard comparison + test coverage
Somzilla issues
…etric-cardinality fix: bound prometheus metric cardinality for wallet/transaction ids
…y-md-private-disclosure feat: harden SECURITY.md for private vulnerability disclosure (issue …
…-mainnet-payment-without-horizon-feesource fix: fail production boot when mainnet payment enabled without Horizo…
…ility-tasks-789-792 feat: Implement security & reliability tasks mux-labs#789-mux-labs#792
…696-wallet-encryption-key-rotation-and-response-hardening feat(key-management): WALLET_ENCRYPTION_KEY rotation + wallet response hardening
fix(users): clean up owned projects/developers/wallets on user deletion
…-nickname-sanitize-uniqueness feat(wallets): sanitize wallet nickname labels & enforce per-owner uniqueness
RecoveryService injects PrismaService directly but RecoveryModule relied on PrismaModule's @global() decorator to resolve it. This fragile coupling breaks test isolation when the global module is not loaded. - Add PrismaModule to RecoveryModule imports array - Add module spec verifying compilation, provider resolution, and that PrismaModule appears in the imports metadata Closes mux-labs#772 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Jambox11
pushed a commit
that referenced
this pull request
Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PrismaModuleimport toRecoveryModuleso it no longer relies on@Global()to resolvePrismaServicerecovery.module.spec.ts) verifying the module compiles in isolation and thatPrismaServiceis resolvable through the module's own import chainProblem
RecoveryServiceinjectsPrismaService, butRecoveryModuledid not explicitly importPrismaModule. It only worked becausePrismaModuleis decorated with@Global(). This is fragile and breaks test isolation when the global module is not loaded.Changes
src/recovery/recovery.module.ts— addedPrismaModuleto theimportsarray and added the corresponding import statementsrc/recovery/recovery.module.spec.ts— new spec that:importsmetadata viaReflect.getMetadataTest plan
npm run test -- --testPathPattern=recovery.module.specpassesCloses #772
🤖 Generated with Claude Code